#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Fail.hpp"
#include <functional>
#include <cstring>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

using namespace std;

static const uint16_t default_port = 8080; //默认端口号
static const int backlog = 32;
using func_t = function<string(const string&)>; //回调函数类型

class tcp_server; //前置声明

class ThreadData
{
public:
    //默认构造
    ThreadData(int sock, string client_msg, tcp_server* tcps)
        : _sock(sock)
        , _client_msg(client_msg)
        , _tcps(tcps)
    {}

    //析构函数
    ~ThreadData()
    {}

public:
    int _sock; //套接字
    string _client_msg;
    tcp_server* _tcps;
};

class tcp_server
{
public:
    //默认构造
    tcp_server(func_t refunc, uint16_t port = default_port)
        : _refunc(refunc)
        , _port(port)
        , _quit(true)
    {}

    //初始化
    void init()
    {     
        //创建套接字
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listen_sock < 0)
        {
            perror("create sock fail");
            exit(SOCKET_FAIL);
        }

        //明确服务器的ip、port
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        //绑定
        if(bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            
            perror("bind fail");
            exit(BIND_FAIL);
        }

        //监听
        if(listen(_listen_sock, backlog) < 0)
        {
            perror("listen fail");
            exit(LISTEN_FAIL);
        }
    }

    void start()
    {
        _quit = false;
        
        //客户端方
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        memset(&client, 0, sizeof(client));
        
        while(!_quit)
        {   
            //忽略子进程回收信号
            signal(SIGCHLD, SIG_IGN);

            //接收连接
            int sock = accept(_listen_sock, (struct sockaddr*)&client, &len);
            if(sock < 0)
            {
                perror("accept fail");
                continue;
            }

            //打印客户端信息
            string client_ip = inet_ntoa(client.sin_addr);
            uint16_t client_port = ntohs(client.sin_port);
            string client_msg = client_ip + ":" + to_string(client_port);

            cout << "获取连接成功> " << sock << " from " << _listen_sock << " client ip " << client_ip << " client port " << client_port << endl;  
            
            //客户端成功建立连接--执行任务
            //v1.只允许一个客服端访问
            //server_plan(sock);

            //v2.多进程访问
            // pid_t pid = fork();
            // //fork失败
            // if(pid == -1)
            // {
            //     perror("fork fail");
            //     continue;
            // }
            // //子进程
            // else if(pid == 0)
            // {
            //     close(_listen_sock);
            //     server_plan(sock, client_msg);

            //     //巧妙写法，不需要父进程wait了，子进程退出了，孙进程成了孤儿进程，此时是孙进程在执行任务，而孙进程是孤儿进程由init进程回收
            //     // if(fork() > 0)
            //     // {
            //     //     exit(0);
            //     // }

            //     exit(0);
            // }

            // //用不到sock，关掉避免文件描述符越来越多
            // close(sock);

            //v3.多线程访问
            pthread_t tid; //线程id
            ThreadData* t_data = new ThreadData(sock, client_msg, this);
            pthread_create(&tid, nullptr, thread_routine, t_data);
            
        }
    }

    //线程执行的函数
    static void* thread_routine(void* args)
    {
        pthread_detach(pthread_self()); //线程分离，不用join了
        ThreadData* t_data = static_cast<ThreadData*>(args);

        t_data->_tcps->server_plan(t_data->_sock, t_data->_client_msg);
        delete t_data;
    }

    //任务
    void server_plan(int sock, string client_msg)
    {
        while(true)
        {   
            char buff[1024];
            ssize_t n = read(sock, buff, sizeof(buff)); //读取
            if(n < 0)
            {
                perror("read fail");
            }
            else if(n == 0)
            {
                cout << "client " << client_msg << " quit" << endl;
                break;
            }
            else
            {
                buff[n] = '\0';
                string message = _refunc(buff);
                cout << "client " << client_msg << "> " << message << endl;
                write(sock, message.c_str(), message.size());
            }
        }
    }

    ~tcp_server()
    {}

private:
    int _listen_sock; //套接字
    uint16_t _port; //端口号
    bool _quit;
    func_t _refunc; //回调函数
};